//@PCSTART@//
package com.bluetane.pc;
//@PCEND@//
/*//@ANDROIDSTART@//
package com.bluetane.android;
//@ANDROIDEND@//*/

import java.util.Iterator;
import java.util.Vector;

//@PCSTART@//
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import com.bluetane.pc.BluetaneEvent.BluetaneEventType;
import com.bluetane.utils.*;
import java.io.IOException;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
//@PCEND@//
/*//@ANDROIDSTART@@//
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import java.util.Set;
import java.util.UUID;
import com.bluetane.android.BluetaneEvent.BluetaneEventType;
//@ANDROIDEND@@//*/

public class BtLocalDevice
{
	/*//@ANDROIDSTART@//
    private BluetoothAdapter localBluetooth;
    private ActivityListener listener;
    private static final int ENABLE_BLUETOOTH = 1;
    private static final int ENABLE_DISCOVERY = 2;
    private String uniqueIDString;
    private UUID uniqueID;
	//@ANDROIDEND@//*/
    //@PCSTART@//
    private BluetoothDevListener devListener = new BluetoothDevListener();
    private DiscoveryAgent discAgent;
    private LocalDevice localDev;
    //private final Object inquiryLock = new Object();
    private final Object serviceDiscoveryLock=new Object();
    private String uuid;
    //@PCEND@//

    private String serviceName;
    Vector<BtRemoteDevice> remoteDevices;//Vector is a threadsafe ArrayList
    private BluetaneEventSource eventSource=new BluetaneEventSource();

    /**
     * Creates Local Device and generates a 32 character hex UUID from the serviceName
     * Android only: Tries to turn on bluetooth and set device to discoverable
     
     @param btServiceName a unique string describing the service.
     */
    public BtLocalDevice(String startServiceName)
    {
        remoteDevices = new Vector<BtRemoteDevice>();
        serviceName = startServiceName;

        /*//@ANDROIDSTART@// 
    	uniqueIDString = UuidHash.uuidHashDash(serviceName);
    	uniqueID = UUID.fromString(uniqueIDString);
        //@ANDROIDEND@//*/
        //@PCSTART@//
        uuid=UuidHash.uuidHash(serviceName);
        //@PCEND@//
    }
    
	/*//@ANDROIDSTART@// 
    public void startBluetooth()
    {
    	localBluetooth = BluetoothAdapter.getDefaultAdapter();
    	if (localBluetooth == null)
    	{
    		// No bluetooth
    	}
    	else
    		enableBluetooth();
    }
    
    public void enableBluetooth()
    {
    	Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    	listener.startActivityForResult(enableBtIntent, ENABLE_BLUETOOTH);
    }

    public void makeDiscoverabe(int seconds)
    {
    	Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
        discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, seconds);
        listener.startActivityForResult(discoverableIntent, ENABLE_DISCOVERY);	
    }
	//@ANDROIDEND@//*/
    
    /**
     * Searches for remote bluetooth devices and generates a statusUpdateEvent of type DevicesFound when it has completed.
     * getDeviceNames() can then be called to retrive the devices found.
     */
    public void findRemotes()
	//@PCSTART@//
 	throws javax.bluetooth.BluetoothStateException
	//@PCEND@//
	{
		/*//@ANDROIDSTART@//
    	Set<BluetoothDevice> pairedRemotes = localBluetooth.getBondedDevices();
    	BluetoothDevice paired;
    	BtRemoteDevice btRemote;
    	Iterator<BluetoothDevice> pairedIterator = pairedRemotes.iterator(); 
    	while (pairedIterator.hasNext())
    	{
    		paired = pairedIterator.next();
    		btRemote = new BtRemoteDevice(paired);
    		if (!remoteDevices.contains(btRemote))
            	remoteDevices.add(btRemote);
    		eventSource.fireEvent(new BluetaneEvent(this, BluetaneEventType.DeviceFound, btRemote));
    	}
    	
    	
    	if (localBluetooth.isDiscovering())
            localBluetooth.cancelDiscovery();
    	BroadcastReceiver discoveryListener = new BroadcastReceiver()
        {
            public void onReceive(Context context, Intent intent)
            {
            	BluetoothDevice remoteDevice;
            	BtRemoteDevice btRemote;
                if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction()))
                {
                	remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    btRemote = new BtRemoteDevice(remoteDevice);
                    if (!remoteDevices.contains(btRemote))
                    	remoteDevices.add(btRemote);
                	eventSource.fireEvent(new BluetaneEvent(this, BluetaneEventType.DeviceFound, btRemote));
                }
            }
        };
        IntentFilter actionFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        listener.registerReceiver(discoveryListener, actionFilter);
        localBluetooth.startDiscovery();

	//@ANDROIDEND@//*/

	//@PCSTART@//
        try{
            localDev = LocalDevice.getLocalDevice();
            discAgent = localDev.getDiscoveryAgent();

            //Find devices the stack/OS already knows about
            processFoundRDs(discAgent.retrieveDevices(DiscoveryAgent.PREKNOWN));//Could have boolean to show these devices are from cache
            processFoundRDs(discAgent.retrieveDevices(DiscoveryAgent.CACHED));

            //Find devices by conducting a search
            discAgent.startInquiry(DiscoveryAgent.GIAC, devListener);//Could have if/gate to determine if a search needs to take place (or just cached devices)

        } catch(javax.bluetooth.BluetoothStateException er){
            //Throw no bluetooth found installed error
            System.err.println("No bluetooth found installed");
            throw er;
        }

//        //Wait for search to finish
//        try
//        {
//            synchronized (inquiryLock) {
//                inquiryLock.wait();
//            }
//        } catch (InterruptedException e) {
//            throw e;
//        }
        //@PCEND@//
    }

    /*//@ANDROIDSTART@//
    //@ANDROIDEND@//*/
    //@PCSTART@//
    public String[] getDeviceNames()
    {
        int size=remoteDevices.size();
        Iterator<BtRemoteDevice> itt=remoteDevices.iterator();
        String[] names = new String[size];
        for(int j=0; j<size; j++)//hasNext can change array length
        {
            names[j] = itt.next().getName();
        }
        return names;
    }

    public BtRemoteDevice getLastDevice()
    {
        return  remoteDevices.lastElement();
    }

    public BtRemoteDevice getDevice(int i)
    {
        return  remoteDevices.elementAt(i);
    }

     private boolean stopDeviceSearch(){//not working
        return discAgent.cancelInquiry(devListener);
    }

    boolean deviceExists(String bluetoothAddress){
        Iterator<BtRemoteDevice> itt=remoteDevices.iterator();
        while(itt.hasNext()){
            if(itt.next().deviceExists(bluetoothAddress)) return true;
        }
        return false;
    }

    private void processFoundRDs(RemoteDevice[] foundRDs)// throws Exception
    {
        for(int i=0;i<foundRDs.length;i++){
            //only do this i device doesn't exist
            if(!deviceExists(foundRDs[i].getBluetoothAddress()))
            {
                newRemoteDevFound(new BtRemoteDevice(foundRDs[i]));//Must deal with exception better
            }
        }
    }

    private void newRemoteDevFound(BtRemoteDevice newDev)
    {
        remoteDevices.add(newDev);
        this.fireEvent(new BluetaneEvent(this,BluetaneEventType.DeviceFound,newDev));
    }
    private void connectIfRunningService(BtRemoteDevice remote)//askRemoteIfRunning
    {
        UUID[] uuidSet = new UUID[1];
        uuidSet[0] = new UUID(uuid,false);
        //Starts service search on remote device
        try {
            discAgent.searchServices(null, uuidSet, remote.remoteDevice, devListener);
            System.out.println("searching...");
        } catch (IOException er) {
            er.printStackTrace();
        }
//
//        //Wait for search to finish
   //This method is ?blocking? i.e. it may take some time to return
//        try
//        {
//            synchronized (serviceDiscoveryLock) {
//                serviceDiscoveryLock.wait();
//            }
//        } catch (InterruptedException e) {
//            System.out.println("Interrupted waiting checking if running");
//        }
//
//        return
        //if then System.out.println("Remote device is not running app");
    }

    void runningRemoteDiscovered(boolean running,String connectionURL){
        if(!running){
            System.out.println("Remote device is not running app");
            System.out.println("Reason given was: "+connectionURL);
            return;
        }
        try{
            StreamConnection port = (StreamConnection) Connector.open(connectionURL);//, READ_WRITE, timeout);
            System.out.println(connectionURL);
            BtConnection conn = new BtConnection(this,port,eventSource);
            this.fireEvent(new BluetaneEvent(this,BluetaneEventType.ConnectionEstablished,conn));
        }catch(IOException e){
            System.err.println("Error connecting to device: cannot open port");
        }

//         synchronized (serviceDiscoveryLock) {
//                serviceDiscoveryLock.notify();
//         }
    }
    //@PCEND@//

    /**
     * Connects to a remote device and fires ConnectionEstablished event.
     * Passes the BtConnection for the remote device as the message in the event.
     * Requires that the remote application is running and is waiting for a connection
     * by calling btLocalDevice.acceptConnection

     @param Remote the BtRemoteDevice to connect to
     */
    public void connect(BtRemoteDevice remoteDevice)
    {
        /*//@ANDROIDSTART@//
    	ConnectThread connectThread = new ConnectThread(localBluetooth, remoteDevice.getDevice(), uniqueID, eventSource);
        //@ANDROIDEND@//*/

        //@PCSTART@//
        connectIfRunningService(remoteDevice);
        //@PCEND@//
    }
    
    /**
     * Opens new thread to wait for a connection from a remote device.
     * Makes app avaliable as a bluetooth service.
     * Fires ConectionEstablished 
     
     @param seconds the number of seconds to wait for a connection
      */
    public void acceptConnection(int seconds)
    {
        long startTime = System.currentTimeMillis();
    	AwaitConnectionThread acceptThread;
        /*//@ANDROIDSTART@//
        acceptThread= new AwaitConnectionThread(localBluetooth, serviceName, uniqueID, eventSource);
        //@ANDROIDEND@//*/
        //@PCSTART@//
        acceptThread= new AwaitConnectionThread(localDev,"btssp://localhost:"+uuid+";name="+serviceName, eventSource);
        //@PCEND@//
    	if ((System.currentTimeMillis() - startTime)/1000 > seconds)
    	{
    		acceptThread.cancel();
    	}

    }

    public void addEventListener(BluetaneListener listener)
    {
        eventSource.addEventListener(listener);
    }
    public void removeEventListener(BluetaneListener listener)
    {
        eventSource.removeEventListener(listener);
    }
    void fireEvent(BluetaneEvent event)
    {
        eventSource.fireEvent(event);
    }


    //@PCSTART@//
    public class BluetoothDevListener implements DiscoveryListener
    {
        @Override
        public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod)
        {
            try
            {
                newRemoteDevFound(new BtRemoteDevice(btDevice));
            }
            catch (Exception e){
                //Should do somthing here
            }
        }

        @Override
        public void inquiryCompleted(int discType)
        {
            System.out.println("inquiryCompleted discType:"+discType);
//            switch(discType){}
//            INQUIRY_COMPLETED
//            INQUIRY_ERROR
//            IINQUIRY_TERMINATED

//            synchronized (inquiryLock) {
//                inquiryLock.notify();
//            }
        }

        @Override
        public void servicesDiscovered(int transID, ServiceRecord[] servRecord)
        {
            System.out.println("servicesDiscovered transID:"+transID+", found "+servRecord.length+" records");
            if (servRecord != null && servRecord.length > 0) {
                runningRemoteDiscovered(true,servRecord[0].getConnectionURL(0, false));
            }
            runningRemoteDiscovered(false,"NULL_RECORDS");
        }

        @Override
        public void serviceSearchCompleted(int transID, int respCode)
        {
            //System.out.println("serviceSearchCompleted transID:"+transID+", respCode:"+respCode);
            switch(respCode)
            {
                case SERVICE_SEARCH_DEVICE_NOT_REACHABLE:
                    runningRemoteDiscovered(false,"DEVICE_NOT_REACHABLE");
                    break;
                case SERVICE_SEARCH_ERROR:
                    runningRemoteDiscovered(false,"ERROR");
                    break;
                case SERVICE_SEARCH_NO_RECORDS:
                    runningRemoteDiscovered(false,"NO_RECORDS");
                    break;
                case SERVICE_SEARCH_TERMINATED:
                    runningRemoteDiscovered(false,"TERMINATED");
                    break;

                case SERVICE_SEARCH_COMPLETED:
                default:
                    break;
            }
        }
    }
    //@PCEND@//
}
